﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Configuration;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using JY.Common.Configurations;
using JY.Common.EntityBase;
using JY.IOC;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;

namespace JY.Repository.EntityFrameworkCore
{
    /// <summary>
    /// 仓储数据上下文
    /// </summary>
    public class DbContextBase : DbContext
    {
        #region # 变量
        public const string DefaultConnection = "DefaultConnection";

        /// <summary>
        /// 同步锁
        /// </summary>
        private static readonly object _Sync;

        /// <summary>
        /// EF（写）上下文对象访问器线程缓存
        /// </summary>
        private static readonly AsyncLocal<DbContextBase> _CommandInstanceCall;

        /// <summary>
        /// EF（读）上下文对象访问器线程缓存
        /// </summary>
        private static readonly AsyncLocal<DbContextBase> _QueryInstanceCall;
        #endregion

        #region # 构造器
        /// <summary>
        /// 静态构造器
        /// </summary>
        static DbContextBase()
        {
            _Sync = new object();
            _CommandInstanceCall = new AsyncLocal<DbContextBase>();
            _QueryInstanceCall = new AsyncLocal<DbContextBase>();
        }
        /// <summary>
        /// 无参构造器
        /// </summary>
        protected DbContextBase()
            : base()
        {
        }

        /// <summary>
        /// 基础构造器
        /// </summary>
        /// <param name="options">The options for this context.</param>
        protected DbContextBase(DbContextOptions options)
            : base(options)
        {
        }
        /// <summary>
        /// 配置
        /// </summary>
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            //开启代理类延迟加载
            optionsBuilder.UseLazyLoadingProxies();
            base.OnConfiguring(optionsBuilder);
        }
        #endregion

        #region # 访问器

        #region EF上下文对象 —— static DbContextBase CommandInstance
        /// <summary>
        /// EF上下文对象
        /// </summary>
        public static DbContextBase CommandInstance
        {
            get
            {
                lock (_Sync)
                {
                    DbContextBase dbContext = _CommandInstanceCall.Value;

                    if (dbContext == null)
                    {
                        dbContext = ResolveMediator.Resolve<DbContextBase>();
                        _CommandInstanceCall.Value = dbContext;
                    }

                    return dbContext;
                }
            }
        }
        #endregion

        #region 释放EF上下文对象 —— static void FreeCommandInstanceCall()
        /// <summary>
        /// 释放EF上下文对象
        /// </summary>
        public static void FreeCommandInstanceCall()
        {
            if (_CommandInstanceCall.Value != null)
            {
                _CommandInstanceCall.Value.Dispose();
            }

            _CommandInstanceCall.Value = null;
        }
        #endregion

        #endregion

        #region # 属性
        #region 实体所在程序集 —— override string EntityAssembly
        /// <summary>
        /// 实体所在程序集
        /// </summary>
        public virtual string EntityAssembly
        {
            get { return EntitySection.Setting.EntityAssembly.Value; }
        }
        #endregion

        #region 实体配置所在程序集 —— override string EntityConfigAssembly
        /// <summary>
        /// 实体配置所在程序集
        /// </summary>
        public virtual string EntityConfigAssembly
        {
            get { return EntitySection.Setting.EntityConfigAssembly.Value; }
        }
        #endregion

        #region 数据表名前缀 —— override string TablePrefix
        /// <summary>
        /// 数据表名前缀
        /// </summary>
        public virtual string TablePrefix
        {
            get { return EntitySection.Setting.EntityTablePrefix.Value; }
        }
        #endregion
        #endregion

        #region # 方法

        #region # 模型创建——void OnModelCreating(DbModelBuilder modelBuilder)
        /// <summary>
        /// 模型创建
        /// </summary>
        /// <param name="modelBuilder">数据模型建造者</param>
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            #region # 验证程序集

            if (string.IsNullOrWhiteSpace(this.EntityAssembly))
            {
                throw new ApplicationException("实体所在程序集未配置！");
            }

            #endregion
            //添加实体的配置
            Assembly entityConfigAssembly = Assembly.Load(this.EntityConfigAssembly);
            modelBuilder.ApplyConfigurationsFromAssembly(entityConfigAssembly);

            var types = Assembly.Load(this.EntityAssembly).GetTypes().Where(x => !x.IsInterface && x.IsPublic && (x.BaseType == typeof(PlainEntity) || x.BaseType == typeof(RootEntity) || x.BaseType == typeof(AggregateRootEntity)) && !x.IsDefined(typeof(NotMappedAttribute))).ToList();
            foreach (Type entityType in types)
            {
                IMutableEntityType mutableEntityType = modelBuilder.Model.FindEntityType(entityType);
                if (mutableEntityType == null)
                {
                    //注册实体类型
                    modelBuilder.Model.AddEntityType(entityType);
                }
            }
            //修改实体名为数据库名
            string tablePrefix = string.IsNullOrWhiteSpace(this.TablePrefix) ? string.Empty : this.TablePrefix;
            foreach (Type entityType in types)
            {
                var entityTypeBuilder = modelBuilder.Entity(entityType);
                entityTypeBuilder.ToTable(tablePrefix + entityType.Name);
            }

            base.OnModelCreating(modelBuilder);
        }

        #endregion

        #region # 创建数据库—— void CreateIfNotExists()
        /// <summary>
        /// 创建数据库
        /// </summary>
        /// <returns></returns>
        public static void CreateIfNotExists()
        {
            CommandInstance.Database.EnsureCreated();
        }
        #endregion
        #endregion
    }
}
